<style lang="less">
	.watermark {
    z-index: 2;
		background: rgba(0, 0, 0, 1);
		.watermark-list {
      height: 172rpx;
      white-space: nowrap;
      .watermark-item {
        margin: 26rpx 12rpx 0;
        width: auto;
        height: 120rpx;
        box-sizing: border-box;
        &:first-child {
          margin-left: 30rpx;
        }
        &:last-child {
          margin-right: 30rpx;
        }
      }
      .watermark-item--active {
        outline: var(--theme) solid 2rpx;
      }
    }
    .handle-view {
      position: relative;
      .movable-view {
        overflow: hidden;
        .movable-image {
          width: 100%;
          height: 100%;
        }
        .watermark-text {
          width: auto;
          height: auto;
          color: #fff;
          font-size: 20px;
        }
      }
      .canvas {
        position: absolute;
        top: -999px;
        left: -999px;
        z-index: -1;
      }
    }
	}
</style>

<template>
	<view class="watermark fixed-view flex flex-col">
		<!-- 操作区 -->
    <view class="handle-view flex-1 flex just-center align-center">
      <movable-area class="movable-view" :style="{ width: movableViewStyle.width, height: movableViewStyle.height }">
        <image class="movable-image" :src="localUrl" :mode="mode" />
        <movable-view v-if="watermarkIndex >= 0" class="watermark-text" :style="{ color: color }" id="watermark" ref="watermark"
          :x="realX" :y="realY" scale direction="all" @click="openTextEdit" @scale="onScale" @touchmove="touchmove" @touchstart="touchstart">
          <view :style="{ transform: `rotate(${deg}deg)` }">{{ waterText }}</view>
        </movable-view>
      </movable-area>
		  <canvas type="2d" class="canvas" id="canvas" :style="{ width: movableImageSize.width + 'px', height: movableImageSize.height + 'px' }"></canvas>
    </view>
		<!-- 底部操作栏 -->
    <handle-bar title="水印" @close="backEdit" @submit="submit">
      <scroll-view class="watermark-list" scroll-x>
        <image v-for="(item, index) in 10" :key="item" class="watermark-item" :class="index === watermarkIndex ? 'watermark-item--active' : ''" src="https://zhuella-1258771799.cos.ap-shanghai.myqcloud.com/test/water.png" mode="heightFix" @click="selectWatermark(index)" />
      </scroll-view>
    </handle-bar>
    <!-- 水印文字编辑 -->
    <text-edit v-if="textEditShow" v-model="waterText" :text-color="color" @changeColor="changeColor" @submit="closeTextEdit" />
	</view>
</template>

<script>
import { createNamespacedHelpers } from 'vuex'
import systemMixin from '@/mixin/system'
import { funcSync, nextTick, debounce } from '@/utils/index'
import HandleBar from '@/biz-components/handle-bar'
import TextEdit from '@/biz-components/text-edit'
const { mapState, mapMutations } = createNamespacedHelpers('app')
/**
 * 加水印
 */
export default {
	name: 'Watermark',
	components: {
    HandleBar,
    TextEdit
  },
	data() {
		return {
			waterText: '史上最帅的水印',
      watermarkIndex: -1, // 选中的水印
      handleViewSize: {
        width: 0,
        height: 0
      },  // 操作区大小
      movableImageSize: {
        width: 375,
        height: 375
      },  // 移动图片大小
      x: 12,	// 水印初始x坐标
      y: 12,	// 水印初始y坐标
      realX: 12,  // 水印真实x坐标(非即时)
      realY: 12,  // 水印真实y坐标(非即时)
      textEditShow: false,  // 显隐水印编辑
      scaleValue: 1,  // 水印缩放倍数
      timestamp: 0,  // 上次缩放的末尾时间
      color: '#fff',  // 文字颜色
      pageX: 0, // 水印相对页面x坐标
      pageY: 0, // 水印相对页面y坐标
      deg: 0, // 当前旋转角度
      lastDeg: 0,  // 上一次旋转角度
      nextDeg: 0, // 下一次旋转角度
		}
	},
	computed: {
    ...mapState(['localUrl', 'mode']),
    // 移动区样式
    movableViewStyle() {
			if (this.mode === 'widthFix') {
				return { width: this.handleViewSize.width + 'px', height: 'auto' }
			}
			return { width: 'auto', height: this.handleViewSize.height + 'px' }
    }
	},
	methods: {
    ...mapMutations(['setLocalUrl', 'setOriginUrl']),
    // 获取操作区
		getHandleView() {
      return new Promise((resolve, reject) => {
				try {
          uni.createSelectorQuery()
            .in(this)
						.select('.handle-view')
						.fields({ size: true })
						.exec(res => {
							resolve(res[0])
						})
				} catch(err) {
					reject(err)
				}
			})
    },
    // 获取移动图片
    getMovableImage() {
      return new Promise((resolve, reject) => {
				try {
          uni.createSelectorQuery()
            .in(this)
						.select('.movable-image')
						.fields({ size: true })
						.exec(res => {
							resolve(res[0])
						})
				} catch(err) {
					reject(err)
				}
			})
    },
    // 获取画布
		getCanvas() {
			return new Promise((resolve, reject) => {
				try {
          uni.createSelectorQuery()
            .in(this)
						.select('.canvas')
						.fields({ node: true, size: true })
						.exec(res => {
							resolve(res[0])
						})
				} catch(err) {
					reject(err)
				}
			})
    },
    // 初始化画布
		async initCanvas() {
      const { width, height, node } = await this.getCanvas()	// canvas高宽、实例
			const ctx = node.getContext('2d')	// canvas上下文
			const { pixelRatio } = uni.getSystemInfoSync()	// 终端屏幕比例
			// 根据比例修正canvas高宽和比例
			node.width = width * pixelRatio
			node.height = height * pixelRatio
      ctx.scale(pixelRatio, pixelRatio)
    },
    // 初始化水印位置
    async initWater() {
      const { width, height } = this.movableImageSize
      const fontSize = 20
      const len = this.waterText.length
      this.x = this.realX = width / 2 - fontSize * len / 2
      this.y = this.realY = height / 2 - fontSize / 2
    },
    // 获取水印
		getWatermark() {
			return new Promise((resolve, reject) => {
				try {
          uni.createSelectorQuery()
            .in(this)
						.select('.watermark-text')
						.boundingClientRect()
						.selectViewport()
						.scrollOffset()
						.exec(res => {
							resolve(res[0])
						})
				} catch(err) {
					reject(err)
				}
			})
    },
    // 加载图片
		loadImage(img) {
			return new Promise((resolve, reject) => {
				try {
					img.onload = () => {
						resolve()
					}
					img.onerror = (e) => {
						uni.showToast({ icon: 'none',	title: '保存失败!' })
						console.error('图片加载失败')
						reject({ error: '图片加载失败' })
					}
				} catch(err) {
					reject(err)
				}
			})
		},
    // 编辑完成
    async submit() {
      uni.showLoading({	title: '保存中' })
      // 未加水印, 直接关闭
      if (this.watermarkIndex === -1) {
        uni.hideLoading()	// 结束加载动画
        this.$emit('close')
        return
      }
      const { left, top, width: waterWidth, height: waterHeight } = await this.getWatermark()	// 获取水印相对定位
      const waterX = left - this.pageX + this.x	// 水印x坐标
			const waterY = top - this.pageY + this.y	// 水印y坐标
      const { width, height, node } = await this.getCanvas()	// canvas高宽、实例
      const ctx = node.getContext('2d')	// canvas上下文
      // 清空画布
			ctx.clearRect(0, 0, width, height)
			// 加图片
			const coverImage = node.createImage()
      coverImage.src = this.localUrl
      // 等待图片加载完毕
      await this.loadImage(coverImage)
      ctx.drawImage(coverImage, 0, 0, this.movableImageSize.width, this.movableImageSize.height)
      // 加水印
      const fontSize = 20 * this.scaleValue
			ctx.font = `${fontSize}px PingFang SC`
      ctx.fillStyle = this.color
      ctx.textAlign = 'center'
      ctx.textBaseline = 'middle'
      const offsetH = fontSize * 0.15 // 文字高度偏移量
      // 水印旋转
      const ox = waterX + waterWidth / 2
      const oy = waterY + waterHeight / 2 + offsetH
      ctx.translate(ox, oy) // 旋转原点
      ctx.rotate(Math.PI / 180 * this.deg)
      ctx.fillText(this.waterText, 0, 0)
      ctx.translate(-ox, -oy)
      const { tempFilePath } = await funcSync(uni.canvasToTempFilePath, { canvas: node, quality: 1 }, uni.hideLoading)
      this.setLocalUrl(tempFilePath)
      this.setOriginUrl(tempFilePath)
      uni.hideLoading()	// 结束加载动画
      this.$emit('submit')
    },
    // 返回编辑页
    backEdit() {
      this.$emit('close')
    },
    // 选择水印
    async selectWatermark(index) {
      // 首次出现
      if (this.watermarkIndex === -1) {
        this.watermarkIndex = index
        await nextTick(this)
        const { left, top } = await this.getWatermark()
        this.pageX = left
        this.pageY = top
        return
      }
      this.watermarkIndex = index
    },
    // 打开文本编辑
    async openTextEdit(e) {
      // 避免双指缩放会触发点击事件
      const timestamp = new Date().getTime() - this.timestamp
      if (timestamp < 100) {
        return
      }
      this.textEditShow = true
      // 记录水印真实坐标
      const { left, top } = await this.getWatermark()
      const offsetX = this.pageX - this.x // 偏移量x
      const offsetY = this.pageY - this.y // 偏移量y
      this.realX = left - offsetX
      this.realY = top - offsetY
    },
    // 关闭文本编辑
    async closeTextEdit() {
      const index = this.watermarkIndex
      this.watermarkIndex = -1
      this.textEditShow = false
      await nextTick(this)
      this.watermarkIndex = index
    },
    // 缩放水印
    onScale: debounce(function(e) {
      this.scaleValue = e.detail.scale
      this.timestamp = new Date().getTime()
    }, 250),
    // 更改文字颜色
    changeColor(color) {
      this.color = color
    },
    touchmove(e) {
      // 过滤单指
      if (e.touches.length < 2) {
        return
      }
      this.deg = (this.lastDeg + this.nextDeg - this.getDeg(e)) % 360
    },
    touchstart(e) {
      // 过滤单指
      if (e.touches.length < 2) {
        return
      }
      this.lastDeg = this.deg
      this.nextDeg = this.getDeg(e)
      e.preventDefault()
    },
    // 获取角度
    getDeg(e) {
      const x1 = e.touches[0].pageX
      const y1 = e.touches[0].pageY
      const x2 = e.touches[1].pageX
      const y2 = e.touches[1].pageY
      const x = x2 - x1
      const y = y1 - y2
      return Math.atan2(y, x) * 180 / Math.PI
    },
	},
	async mounted() {
    this.handleViewSize = await this.getHandleView()
    await nextTick(this)
    this.movableImageSize = await this.getMovableImage()
    this.initWater()
    await nextTick(this)
    this.initCanvas()
	}
}
</script>